package org.lds.message.center.common.annotation;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.lds.message.center.common.util.ExceptionUtil;
import org.lds.message.center.common.util.JsonUtil;
import org.lds.message.center.common.util.UUIDUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
public class LogAspect {

    @Pointcut("@annotation(Log)")
    public void logPointcut() {
    }

    @Around("logPointcut() && @annotation(logAnnotation)")
    public Object execute(ProceedingJoinPoint joinPoint, Log logAnnotation) throws Throwable {
        String traceId = UUIDUtils.simpleUuid();

        String name = logAnnotation.value();
        if (StringUtils.isEmpty(name)) name = joinPoint.getSignature().getName();

        Map<String, Object> request = argsMap(joinPoint);

        log.info("[{}]【{}】 start. 请求:{}", traceId, name, JsonUtil.toJson(request));
        long startTime = System.currentTimeMillis();

        try {
            Object result = joinPoint.proceed();
            recordEnd(traceId, name, startTime, request, result);
            return result;
        } catch (Throwable e) {
            recordEnd(traceId, name, startTime, request, ExceptionUtil.errorMessage(e));
            throw e;
        }
    }

    private void recordEnd(String traceId, String name, long startTime, Map<String, Object> request, Object response) {
        long endTime = System.currentTimeMillis();
        log.info("[{}]【{}】 end. 耗时{}ms 请求:{} 响应:{}", traceId, name, endTime - startTime, JsonUtil.toJson(request), JsonUtil.toJson(response));
    }

    private Map<String, Object> argsMap(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) return null;

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Parameter[] parameters = methodSignature.getMethod().getParameters();
        if (parameters == null || parameters.length != args.length) return null;

        Map<String, Object> map = new HashMap<>();

        for (int i = 0, size = parameters.length; i < size; i++) {
            Parameter parameter = parameters[i];
            Object value = args[i];
            map.put(parameter.getName(), value);
        }
        return map;
    }

}
